/**
 * The contents of this file are subject to the license and copyright detailed in the LICENSE and NOTICE files at the root of the source tree and available online at
 * http://www.askosi.org/license.html
 */
package be.destin.beans;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.List;

import org.apache.log4j.Logger;

import be.destin.skos.core.Term;
import be.destin.util.Util;

public class Reflector {

	public final static char substBeg = '[';
	public final static char substEnd = ']';

	/** log4j category */
	private static Logger log = Logger.getLogger(Reflector.class);

	public static String getStringResult(Object result) {
		if (result == null) return null;
		if (result instanceof List) {
			List<Object> resList = (List<Object>)result;
			if (resList.isEmpty()) return null;
			result = resList.get(0);
		}
		if (result instanceof Term) {
			return ((Term)result).getLabel();
		}
		return String.valueOf(result);
	}

	public static String getBeanField(Object bean, String fieldName,
			String userLanguage) {
		String value = null;
		// Get the class type of the bean.
		Class<?> beanClass = bean.getClass();
		
		int pLang = fieldName.indexOf('@'); 
		if (pLang >= 0 ) {
			userLanguage = fieldName.substring(pLang+1);
			fieldName = fieldName.substring(0, pLang);
		}
		
		String methodName = "get" + fieldName.substring(0, 1).toUpperCase()
		+ fieldName.substring(1);
		Method beanMethod = null;
		if (userLanguage != null) {
			try {
				// Get the method to be invoked
				Class<?>[] string = { String.class, boolean.class };
				beanMethod = beanClass.getMethod(methodName, string);
				// Invoke the method against the bean with the parameter
				// arguments
				Object[] language = { userLanguage, false };
				Object result = beanMethod.invoke(bean, language);
				value = getStringResult(result);
			} catch (IllegalAccessException e) {
				// Empty catch.
			} catch (InvocationTargetException e) {
				// Empty catch.
			} catch (NoSuchMethodException e) {
				// Empty catch.
			}
			if (value == null) {
				try {
					// Get the method to be invoked
					Class<?>[] string = { String.class };
					beanMethod = beanClass.getMethod(methodName, string);
					// Invoke the method against the bean with the parameter
					// arguments
					Object[] language = { userLanguage };
					Object result = beanMethod.invoke(bean, language);
					value = getStringResult(result);
				} catch (IllegalAccessException e) {
					// Empty catch.
				} catch (InvocationTargetException e) {
					// Empty catch.
				} catch (NoSuchMethodException e) {
					// Empty catch.
				}
			}
		}
		if (value == null) {
			try {
				// Get the method to be invoked
				beanMethod = beanClass.getMethod(methodName, (Class<?>[]) null);
				// Invoke the method against the bean with the parameter
				// arguments
				Object result = beanMethod.invoke(bean, (Object[]) null);
				value = getStringResult(result);
			} catch (IllegalAccessException e) {
				log.debug(e.toString() + " with " + fieldName + " in "
						+ bean.toString());
			} catch (InvocationTargetException e) {
				log.debug(e.toString() + " with " + fieldName + " in "
						+ bean.toString());
			} catch (NoSuchMethodException e) {
				log.debug(e.toString() + " with " + fieldName + " in "
						+ bean.toString());
			}
		}
		return value;
	}

	/**
	 * This function returns a message with {field with a getter} substituted with information of this entry
	 * return the message
	 */
	public static String substitute(Object bean, String input, String userLanguage) {
		int fromIndex = 0;
		int i = 0;
		StringBuffer output = new StringBuffer();
		while ( (i = input.indexOf(substBeg, fromIndex)) >= 0 ) {
			output.append(input.substring(fromIndex,i));
			fromIndex = i+1;
			int j = input.indexOf(substEnd, fromIndex);
			if (j > i) {
				String nameParam = input.substring(i+1,j);
				if (nameParam.length() > 0) {
					String result = getBeanField(bean, nameParam, userLanguage);
					if (result != null) output.append(result);
				}
				fromIndex = j+1;
			}
			else output.append(substBeg);
		}
		output.append(input.substring(fromIndex));
		return output.toString();
	}

	/**
	 * This function returns a message with {field with a getter} substituted with information of this entry.
	 * 
	 * @param applicationRoot	String with the beginning of URLs for the involved application. Will be inserted for [root]
	 * @param bean	Object for which attributes can be get
	 * @param input	String with the template where [field] references can appear
	 * @param userLanguage	Optional parameter to the getters indicating user language
	 * @return	String with a text where [field] placeholders are replaced in the context of the user language
	 */
	public static String substituteURL(String applicationRoot, Object bean, String input, String userLanguage) {
		Object[] beans = new Object[1];
		beans[0] = bean ;
		return substituteURL(applicationRoot,beans,input,userLanguage);
	}
	
	public static String substituteURL(String applicationRoot, Object[] beans, String input, String userLanguage) {
		int fromIndex = 0;
		int i = 0;
		StringBuffer output = new StringBuffer();
		while ( (i = input.indexOf(substBeg, fromIndex)) >= 0 ) {
			output.append(input.substring(fromIndex,i));
			fromIndex = i+1;
			int j = input.indexOf(substEnd, fromIndex);
			if (j > i) {
				String nameParam = input.substring(i+1,j);
				if ("root".equals(nameParam) && (applicationRoot != null)) {
					output.append(applicationRoot);
				}
				else {
					String result = null;
					for (int b=0; b<beans.length; b++) {
						if (beans[b]==null) continue;
						result = getBeanField(beans[b], nameParam, userLanguage);
						if (result != null) {
							if (nameParam.toLowerCase().indexOf("url") >= 0) {
								output.append(result); // Text is an URL: what has to be protected is already protected
							} else { // Characters to be protected
								try {
									output.append(URLEncoder.encode(result,"UTF-8"));
								} catch (UnsupportedEncodingException e) {
									log.error(e.toString() + " with " + input + " in " + beans[b].toString());
								}
							}
							break;
						}
					}
				}
				fromIndex = j+1;
			}
			else output.append(substBeg);
		}
		output.append(input.substring(fromIndex));
		return output.toString();
	}

	/**
	 * This function returns a message with {field with a getter} substituted with information of this entry
	 * return the message
	 */
	public static String substituteAttribute(Object bean, String input, String userLanguage) {
		int fromIndex = 0;
		int i = 0;
		StringBuffer output = new StringBuffer();
		while ( (i = input.indexOf(substBeg, fromIndex)) >= 0 ) {
			output.append(input.substring(fromIndex,i));
			fromIndex = i+1;
			int j = input.indexOf(substEnd, fromIndex);
			if (j > i) {
				String nameParam = input.substring(i+1,j);
				String result = getBeanField(bean, nameParam, userLanguage);
				try {
					if (result != null) output.append(Util.escapeHTMLEntities(result));
				} catch (Exception e) {
					log.error(e.toString() + " with " + input + " in "
							+ bean.toString());
				}
				fromIndex = j+1;
			}
			else output.append(substBeg);
		}
		output.append(input.substring(fromIndex));
		return output.toString();
	}

}
